﻿module Leaves.NeuroCryptography.Blocks

open System
open Leaves.NeuroCryptography.NeuralNetworks
open Leaves.NeuroCryptography.Cryptography
open Leaves.NeuroCryptography.KeyGenerators
open Leaves.NeuroCryptography.ConversionLayers

let OutputAlphabet =
    let alphabet = seq {
        for digit in '0'..'9' do
            yield digit

        for letter in 'A'..'Z' do
            yield letter
    }

    alphabet |> Seq.toList

let InputAlphabet =
    let alphabet = seq {
        for digit in '0'..'9' do
            yield digit

        for letter in 'A'..'Z' do
            yield letter

        for letter in 'a'..'z' do
            yield letter

        yield ' '
    }

    alphabet |> Seq.toList

let CreateFixedLengthHashFunction inputLength outputLength userKey =
    let digitalUserKey = FloatifyString userKey InputAlphabet
    let keyGenerator = CreateKeyGenerator digitalUserKey
    let neuralNetwork = CreateHashNetwork inputLength outputLength

    ReconfigureHashNeuralNetwork keyGenerator neuralNetwork

    let hashFunction (inputValue : string) =
        if inputValue.Length <> inputLength then
            failwithf "Input length is %d instead of %d" inputValue.Length inputLength

        let digitalInput = FloatifyString inputValue InputAlphabet
        let digitalOutput = ProcessNetwork neuralNetwork digitalInput

        DefloatifyString digitalOutput OutputAlphabet

    hashFunction

let CreateVariableLengthHashFunction outputLength userKey =
    let defaultInputLength = 32

    let neuralNetwork = CreateHashNetwork defaultInputLength outputLength
    
    let hashFunction (inputValue : string) =
        let mutable nextUserKey = FloatifyString userKey InputAlphabet
        let keyGenerator = CreateKeyGenerator nextUserKey
        
        let paddedInput =
            let modus = inputValue.Length % defaultInputLength
            if modus = 0 then
                inputValue
            else
                inputValue.PadRight(defaultInputLength, InputAlphabet.[0])

        let partsCount = paddedInput.Length / defaultInputLength
        for partIndex in 0..(partsCount - 1) do
            let part =
                seq {
                    for charIndex in (partIndex * defaultInputLength)..((partIndex + 1) * defaultInputLength - 1) do
                        yield paddedInput.[charIndex]
                } |> Seq.toArray |> String.Concat

            let digitalPart = FloatifyString part InputAlphabet

            ReconfigureKeyGenerator keyGenerator nextUserKey
            ReconfigureHashNeuralNetwork keyGenerator neuralNetwork

            nextUserKey <- ProcessNetwork neuralNetwork digitalPart

        DefloatifyString nextUserKey OutputAlphabet

    hashFunction
